package org.acghub.mtdb.core;

import org.acghub.mtdb.common.exception.DeadLockError;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

/**
 * @author zhouhe
 */
abstract class Lockey implements Comparable<Lockey> {

    private final int tableLockId;
    private final Comparable key;
    private final int hashcode;
    private ReadWriteLock rwLock;

    protected Lockey(int id, Comparable key) {
        this.tableLockId = id;
        this.key = key;
        this.hashcode = id ^ (id << 16) ^ key.hashCode();
    }

    @Override
    public int compareTo(Lockey o) {
        int x = tableLockId - o.tableLockId;
        return x != 0 ? x : key.compareTo(o.key);
    }

    @Override
    public int hashCode() {
        return hashcode;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (obj instanceof Lockey lk) {
            return lk.tableLockId == tableLockId && lk.key.equals(key);
        }
        return false;
    }

    @Override
    public String toString() {
        return "Lockey{" +
                "tableLockId=" + tableLockId +
                ", key=" + key +
                '}';
    }

    void allocateLock() {
        if (null == rwLock) {
            rwLock = newRwlock();
        }
    }

    void deallocateLock() {
        rwLock = null;
    }

    protected abstract ReadWriteLock newRwlock();

    public int getTableLockId() {
        return tableLockId;
    }

    public Comparable getKey() {
        return key;
    }

    private Lock readLock() {
        return rwLock.readLock();
    }


    private Lock writeLock() {
        return rwLock.writeLock();
    }

    boolean rTryLock() {
        return readLock().tryLock();
    }

    boolean wTryLock() {
        return writeLock().tryLock();
    }

    void rLock() {
        try {
            readLock().lockInterruptibly();
        } catch (InterruptedException e) {
            throw new DeadLockError(String.format("table %d, key %s", tableLockId, key));
        }
    }

    void wLock() {
        try {
            writeLock().lockInterruptibly();
        } catch (InterruptedException e) {
            throw new DeadLockError(String.format("table %d, key %s", tableLockId, key));
        }
    }

    void rUnlock() {
        readLock().unlock();
    }

    void wUnlock() {
        writeLock().unlock();
    }
}
